package com.marinesky.mstools.codec;
/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
             .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         佛祖保佑       永无BUG
*/
//          佛曰:
//                  写字楼里写字间，写字间里程序员；
//                  程序人员写程序，又拿程序换酒钱。
//                  酒醒只在网上坐，酒醉还来网下眠；
//                  酒醉酒醒日复日，网上网下年复年。
//                  但愿老死电脑间，不愿鞠躬老板前；
//                  奔驰宝马贵者趣，公交自行程序员。
//                  别人笑我忒疯癫，我笑自己命太贱；
//                  不见满街漂亮妹，哪个归得程序员？

import com.marinesky.mstools.common.Constant;
import org.apache.commons.lang.StringUtils;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;

/**
 * ================================================
 * ❶.
 * ❷.
 * ❸.
 * ❹.
 * ❺.
 * ❻.
 * ================================================
 */
public class Base64 {
    public static  char[] base64EncodeChars={
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
            'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
            'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
            'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
            'w', 'x', 'y', 'z', '0', '1', '2', '3',
            '4', '5', '6', '7', '8', '9', '+', '/'
    };
    public static  byte[] base64DecodeChars={
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
            -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
            -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
    };


    /**
     * Base64加密步骤
     * 第一步，将每三个字节作为一组，一共是24个二进制位。(如字节数不够,则补=)
     * 第二步，将这24个二进制位分为四组，每个组有6个二进制位。
     * 第三步，在每组前面加两个00，扩展成32个二进制位，即四个字节。不满足4字节的补=号(缺1补1)
     * 第四步，根据下表，得到扩展后的每个字节的对应符号，这就是Base64的编码值。
     * @param data  --需要编码的字节数组
     * @return
     */
    public static String  encode(byte[] data){
        StringBuffer stringBuffer=new StringBuffer();
        int length=data.length;
        int i=0;
        int b1, b2, b3;
        while (i<length){
            //byte转int,并清空高24为0
            //第一个原始字节
            b1= data[i++] & 0xFF;
            if(i==length){
                //判断是否只有一个字节
                //第一个字符(取前六位,并在前追加两个00(右移2位实现),然后为一个字节,最后得到对应的符号)
                stringBuffer.append(base64EncodeChars[b1>>>2]);
                //第二个字符(取b1后2位,然后后面加4位,前面补两个0)
                //由于只有一个字节,简化操作先清空高26位,再左移4位,实现后面补4个0
                stringBuffer.append(base64EncodeChars[(b1&0x3)<<4]);
                //由于不存在b2,b3,所以直接追加等号
                stringBuffer.append("==");
                break;
            }
            //第二个原始字节
            b2= data[i++] & 0xFF;
            if (i==length){
                //第一个字符(取前六位,并在前追加两个00(右移2位实现),然后为一个字节,最后得到对应的符号)
                stringBuffer.append(base64EncodeChars[b1>>>2]);
                //第二个字符(第一个原始字节的后2位与第二个原始字节的前4字节,然后前面追加2个0)
                stringBuffer.append(base64EncodeChars[((b1 & 0x3)<<4) | ((b2 & 0xf0)>>>4)]);
                //第三个字符(第二个原始字符的后4字节,后面加两个0(左移2位),前面加两个0)
                stringBuffer.append(base64EncodeChars[((b2 & 0xf)<<2)]);
                //第四个字符
                stringBuffer.append("=");
                break;
            }
            //第三个原始字节
            b3= data[i++] & 0xFF;
            //第一个字符(取前六位,并在前追加两个00(右移2位实现),然后为一个字节,最后得到对应的符号)
            stringBuffer.append(base64EncodeChars[b1>>>2]);
            //第二个字符(第一个原始字节的后2位与第二个原始字节的前4字节,然后前面追加2个0)
            stringBuffer.append(base64EncodeChars[((b1 & 0x3)<<4) | ((b2 & 0xf0)>>>4)]);
            //第三个字符(第二个原始字节后4位与第三个原始字节前2位,然后再前面追加2个0)
            stringBuffer.append(base64EncodeChars[((b2 & 0xf)<<2) | ((b3 & 0xc0)>>>6)]);
            //第四个字符(第三个原始字节后6位,然后再前面追加2个0(前面两位已经置0了,不需要右移了))
            stringBuffer.append(base64EncodeChars[((b3 & 0x3f))]);

        }
        return stringBuffer.toString();
    }


    /**
     * Base64加密
     * @param dataStr -- 需要加密的字符串
     * @param charset -- 字符编码
     * @return
     */
    public static String encode(String dataStr,String charset ){
        String result=null;
        try {
            if(StringUtils.isBlank(charset) || !Charset.isSupported(charset)){
                throw new IllegalArgumentException(" the charset is blank or not support! ");
            }
            byte[] data=dataStr.getBytes(charset);
            result=encode(data);
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Base64解码
     * 第一步，将解密的字符分成字节数组,每四个一轮
     * 第二步，将字节4个一转换成3个原始字节,并追加完整
     * 第三步，追加并转成单字节的ISO8859_1 byte数组
     * @param base64Str --Base64编码后的字符串
     * @return
     */
    public static byte[] decode(String base64Str){
        StringBuffer data=new StringBuffer();
        try{
            byte[] data64=base64Str.getBytes(Constant.Charset.US_ASCII);
            int length=data64.length;
            //编码后的四字节
            int b1,b2,b3,b4;
            int i=0;
            while (i<length){
                do {
                    //第一个字节(如果是-1,也就是=就继续到不是为止)
                    b1=base64DecodeChars[data64[i++]];
                }while (i<length && b1==-1);
                //如果一直都是-1则终止,为空
                if(b1==-1) break;
                do {
                    //第二个字节
                    b2 = base64DecodeChars[data64[i++]];
                } while (i < length && b2 == -1);
                if (b2 == -1) break;
                data.append((char)((b1<<2) | ((b2 & 0x30)>>>4)));
                do {
                    b3 = data64[i++];
                    if (b3 == 61) return data.toString().getBytes(Constant.Charset.ISO8859_1);
                    b3 = base64DecodeChars[b3];
                } while (i < length && b3 == -1);
                if (b3 == -1) break;
               data.append((char) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2)));

                do {
                    b4 = data64[i++];
                    if (b4 == 61) return data.toString().getBytes(Constant.Charset.ISO8859_1);
                    b4 = base64DecodeChars[b4];
                } while (i < length && b4 == -1);
                if (b4 == -1) break;
                data.append((char) (((b3 & 0x03) << 6) | b4));
            }
            return data.toString().getBytes(Constant.Charset.ISO8859_1);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Base64解码
     * @param base64Str  -- Base64编码后的字符串
     * @param charset    -- 字符编码
     * @return
     */
    public static String decode(String base64Str,String charset){
        String result=null;
        try {
            if(StringUtils.isBlank(charset) || !Charset.isSupported(charset)){
                throw new IllegalArgumentException(" the charset is blank or not support! ");
            }
            byte[] data=decode(base64Str);
            result=new String(data,charset);
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

}
